package br.com.comanda.dao.autosql;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import android.util.Log;
import br.com.comanda.dao.autosql.annotations.Column;
import br.com.comanda.dao.autosql.annotations.ForeignKey;
import br.com.comanda.dao.autosql.annotations.Id;
import br.com.comanda.dao.autosql.annotations.Table;
import br.com.comanda.dao.autosql.annotations.Unique;
import br.com.comanda.dao.autosql.exception.AutoGenerateException;
import br.com.comanda.dao.autosql.exception.TableNotNamedException;
import br.com.comanda.pojo.Comanda;
import br.com.comanda.pojo.Configuracao;
import br.com.comanda.util.ArrayUtils;
import br.com.comanda.util.Constantes;

/**
 * Classe de geracao automatica de script para gerar a base de dados
 * 
 * @author thiago
 * @since 09/01/2013
 */
public class ScriptGenerator {

	private static Class<?> mappedClasses[] = { Configuracao.class,
			Comanda.class };

	/**
	 * Gera array de Strings para deletar as tabelas
	 */
	public String generateDeleteScript() {
		StringBuilder deleteScript = new StringBuilder();

		for (Class<?> clazz : mappedClasses()) {
			if (clazz.isAnnotationPresent(Table.class)) {
				Table tabela = clazz.getAnnotation(Table.class);
				deleteScript.append(String.format("DROP TABLE IF EXISTS %s; ",
						tabela.name()));
			} else {
				throw new TableNotNamedException(
						"A entidade nao esta anotada com anotacao Tabela");
			}
		}

		return deleteScript.toString();
	}

	/**
	 * Gera script para criacao das tabelas do banco de dados
	 */
	public String[] generateCreateScript() {
		List<String> list = new ArrayList<String>();

		try {
			for (Class<?> clazz : mappedClasses()) {
				Table tabela = clazz.getAnnotation(Table.class);

				if (tabela == null) {
					throw new IllegalArgumentException(String.format(
							"A classe %s nao esta anotada com @Table",
							clazz.getName()));
				}

				StringBuilder createTable = new StringBuilder(
						createTable(tabela));
				StringBuilder foreignKeyDefinitions = new StringBuilder();
				Field[] fields = clazz.getDeclaredFields();

				for (Field field : fields) {
					if (field.isAnnotationPresent(Column.class)) {
						Column coluna = field.getAnnotation(Column.class);

						if (field.isAnnotationPresent(Id.class)) {
							Id id = field.getAnnotation(Id.class);
							createTable.append(String.format(
									"%s INTEGER PRIMARY KEY %s, ", coluna
											.name(),
									id.autoIncrement() ? "AUTOINCREMENT" : ""));
						} else if (field.isAnnotationPresent(Unique.class)) {
							createTable.append(addColumnDeclaration(field,
									coluna).replace(",", " UNIQUE,"));
						} else if (field.isAnnotationPresent(ForeignKey.class)) {
							ForeignKey foreignKey = field
									.getAnnotation(ForeignKey.class);
							foreignKeyDefinitions
									.append(String
											.format(", FOREIGN KEY(%s) REFERENCES %s(%s) ON DELETE CASCADE",
													coluna.name(), foreignKey
															.tableReference(),
													foreignKey
															.columnReference()));
							createTable.append(addColumnDeclaration(field,
									coluna));
						} else {
							createTable.append(addColumnDeclaration(field,
									coluna));
						}
					} else {
						continue;
					}
				}

				createTable.deleteCharAt(createTable.length() - 2);
				createTable.append(foreignKeyDefinitions.toString());
				createTable.append(");");
				list.add(createTable.toString());
			}
		} catch (AutoGenerateException e) {
			Log.e(Constantes.TAG_LOG, e.getMessage());
		}

		return ArrayUtils.toStringArray(list);
	}

	private String createTable(Table tabela) {
		return String.format("CREATE TABLE %s(", tabela.name());
	}

	private String addColumnDeclaration(Field field, Column coluna)
			throws AutoGenerateException {
		try {
			Class<?> type = field.getType();

			if (type.isPrimitive()) {
				type = Wrapper.box(type);
			}

			String declarationType = Wrapper.sqliteWrapper(type);

			return String.format("%s %s, ", coluna.name(), declarationType);
		} catch (Exception e) {
			throw new AutoGenerateException("Falha ao gerar coluna", e);
		}
	}

	public static Class<?>[] mappedClasses() {
		return mappedClasses;
	}
}
